﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Threading;

namespace ZOIL.DataModel
{
    /// <summary>
    /// Implementation of IPersistableCollection using no Database in the Background
    /// </summary>
    public class LocalOnlyCollection : IPersistableCollection
    {
        #region Fields
        protected readonly ObservableCollection<Tuple<IPersistable, IVisualProperties>> InnerCollection;
        private long _lastId;
        protected long GetNextId()
        {
            return Interlocked.Increment(ref _lastId);
        }

        protected static readonly IEqualityComparer<Tuple<IPersistable, IVisualProperties>> TupleIdComparer = 
            EqualityComparer.Create<Tuple<IPersistable, IVisualProperties>>(
                            (t1, t2) => t1.Item1.DatabaseId == t2.Item1.DatabaseId,
                                   t => t.Item1.DatabaseId.GetHashCode()
                        );
        #endregion

        public LocalOnlyCollection()
        {
            InnerCollection = new ObservableCollection<Tuple<IPersistable, IVisualProperties>>();
        }

        #region IPersistableCollection
        public virtual IPersistable GetPersistable(long id)
        {
            return GetPersistableAndVisualProperties(id).Item1;
        }

        public virtual IVisualProperties GetVisualProperties(long id)
        {
            return GetPersistableAndVisualProperties(id).Item2;
        }

        public virtual Tuple<IPersistable, IVisualProperties> GetPersistableAndVisualProperties(long id)
        {
            return InnerCollection.Single(t => t.Item1.DatabaseId == id);
        }
        public virtual Tuple<IPersistable, IVisualProperties> GetPersistableAndVisualPropertiesIfExisting(long id)
        {
            return InnerCollection.SingleOrDefault(t => t.Item1.DatabaseId == id);
        }

        public virtual void Add(IPersistable p, IVisualProperties prop)
        {
            Add(Tuple.Create(p, prop));
        }

        public void AddRange(IEnumerable<Tuple<IPersistable, IVisualProperties>> items)
        {
            foreach (var item in items)
            {
                Add(item);
            }
        }

        public virtual event NotifyCollectionChangedEventHandler CollectionChanged
        {
            add { InnerCollection.CollectionChanged += value; }
            remove { InnerCollection.CollectionChanged -= value; }
        }

        public virtual void Dispose()
        {}

        public virtual IEnumerator<Tuple<IPersistable, IVisualProperties>> GetEnumerator()
        {
            return InnerCollection.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public virtual void Add(Tuple<IPersistable, IVisualProperties> item)
        {
            if (item.Item1.DatabaseId != default(long))
            {
                throw new ArgumentException("Passed Persistable already has an ID!", "item");
            }
            if (item.Item2.BelongsToDatabaseId != default(long))
            {
                throw new ArgumentException("Passed VisualProperties already has an ID!", "item");
            }

            var id = GetNextId();
            item.Item1.DatabaseId = id;
            item.Item2.BelongsToDatabaseId = id;
            InnerCollection.Add(item);
        }

        public virtual void Clear()
        {
            InnerCollection.Clear();
        }

        public virtual bool Contains(Tuple<IPersistable, IVisualProperties> item)
        {
            return InnerCollection.Contains(item, TupleIdComparer);
        }

        public virtual void CopyTo(Tuple<IPersistable, IVisualProperties>[] array, int arrayIndex)
        {
            InnerCollection.CopyTo(array, arrayIndex);
        }

        public virtual bool Remove(Tuple<IPersistable, IVisualProperties> item)
        {
            if (InnerCollection.Remove(item))
            {
                item.Item1.DatabaseId = default(long);
                item.Item2.BelongsToDatabaseId = default(long);
                return true;
            }
            return false;
        }

        public string BackendName { get { return "None"; } }

        public virtual bool Remove(long id)
        {
            var item = GetPersistableAndVisualPropertiesIfExisting(id);
            return item != null && Remove(item);
        }

        public virtual int Count { get { return InnerCollection.Count; } }
        public virtual bool IsReadOnly { get { return false; } }
        #endregion

        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged
        {
            add { ((INotifyPropertyChanged)InnerCollection).PropertyChanged += value; }
            remove { ((INotifyPropertyChanged) InnerCollection).PropertyChanged -= value; }
        }

        #endregion
    }
}